﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.IO;
using System.Xml;

namespace TVRageDAL
{
    public class TVDAL
    {
        public string TvRageApiUrlQuickinfo { get; set; }
        public string TvRageApiUrlEpisodeinfo { get; set; }
        public string TvRageApiUrlSearch { get; set; }

        public TVDAL()
        {
            TvRageApiUrlQuickinfo = "http://services.tvrage.com/tools/quickinfo.php?show=";
            TvRageApiUrlEpisodeinfo = "http://services.tvrage.com/feeds/episode_list.php?sid=";
            TvRageApiUrlSearch = "http://services.tvrage.com/feeds/search.php?show=";
        }

        /// <summary>
        /// Searches for a list of matching shows
        /// </summary>
        /// <param name="search">Name of the show to search for</param>
        /// <returns>An array of ShowInformation</returns>
        public ShowInformation[] SearchShows(string search)
        {
            var ub = new UriBuilder(TvRageApiUrlSearch + search);
            var uri = Uri.EscapeUriString(ub.Uri.ToString());
            var res = GetPage(uri);

            try
            {
                var xml = new XmlDocument();
                xml.LoadXml(res);
                var showCount = xml.GetElementsByTagName("show").Count;

                if (showCount > 0)
                {
                    var showInfoArray = new ShowInformation[showCount];
                    var sCounter = 0;

                    foreach (XmlElement show in xml.GetElementsByTagName("show"))
                    {
                        showInfoArray[sCounter] = new ShowInformation
                                                        {
                                                            ShowId =
                                                                int.Parse(show.SelectSingleNode("showid").InnerText),
                                                            Name = show.SelectSingleNode("name").InnerText,
                                                            SearchName = search,
                                                            ShowUrl = show.SelectSingleNode("link").InnerText
                                                        };
                        sCounter++;
                    }

                    return showInfoArray;
                }
                
                return null;
            } 
            catch (XmlException e) 
            {
                return null;                
            }
        }

        /// <summary>
        /// Gets single show information
        /// </summary>
        /// <param name="showName">Name of the show to look</param>
        /// <param name="exact">True if show name is exact, false if approximate</param>
        /// <param name="withEpisodes">True to return episode information, default is false</param>
        /// <returns>ShowInformation type</returns>
        public ShowInformation GetShowInfo(string showName, bool exact = false, bool withEpisodes = false)
        {
            var show = new ShowInformation();
            var url = TvRageApiUrlQuickinfo + showName;

            if (exact)
                url = url + "&exact=1";
            
            var res = GetPage(url);

            try
            {
                res = res.Substring(5); // Remove <pre> tag
                var dic = ParseOutputInformation(res);
                show.ShowId = int.Parse(dic["Show ID"]);
                show.Name = dic["Show Name"];
                show.ShowUrl = dic["Show URL"];

                if (withEpisodes)
                {
                    var ep = GetShowEpisodes(show.ShowId);

                    if (ep != null)
                        show.Episodes = ep;
                }

                return show;
            }
            catch (XmlException e)
            {
                return null;
            }
        }

        /// <summary>
        /// Gets information of a single episode
        /// </summary>
        /// <param name="showName">Name of the show</param>
        /// <param name="season">Number of season</param>
        /// <param name="episode">Number of episode</param>
        /// <returns>EpisodeInformation type</returns>
        public EpisodeInformation GetEpisodeInfo(string showName, string season, string episode)
        {
            var ep = new EpisodeInformation();
            var url = TvRageApiUrlQuickinfo + showName + "&ep=" + season + "x" + episode;
            var res = GetPage(url);

            try
            {
                res = res.Substring(5); // Remove <pre> tag
                var dic = ParseOutputInformation(res);
                var delim = new string[1];
                delim[0] = "^";

                if (dic.ContainsKey("Episode Info"))
                {
                    var epi = dic["Episode Info"].Split(delim, StringSplitOptions.None);

                    ep.Name = epi[1];
                    ep.AirDate = epi[2];

                    return ep;
                }
                
                return null;
            }
            catch (XmlException e)
            {
                return null;
            }
        }

        /// <summary>
        /// Gets episodes information for a show
        /// </summary>
        /// <param name="showId">Show Id</param>
        /// <returns>Episodes</returns>
        public EpisodeInformation[] GetShowEpisodes(int showId)
        {
            var res = GetPage(TvRageApiUrlEpisodeinfo + showId);
            var xml = new XmlDocument();

            try
            {
                xml.LoadXml(res);
                var episodeCount = xml.GetElementsByTagName("episode").Count;

                if (episodeCount > 0)
                {
                    var episodeArray = new EpisodeInformation[episodeCount];
                    var seasonCount = xml.GetElementsByTagName("Season").Count;
                    var sCounter = 1;
                    var eCounter = 0;

                    if (seasonCount > 0)
                    {
                        foreach (XmlElement season in xml.GetElementsByTagName("Season"))
                        {
                            foreach (XmlElement episode in season)
                            {
                                if (episode != null)
                                {
                                    var epdate = episode.SelectSingleNode("airdate").InnerText;
                                    var epnum = episode.SelectSingleNode("seasonnum").InnerText;
                                    var eptitle = episode.SelectSingleNode("title").InnerText;

                                    episodeArray[eCounter] = new EpisodeInformation
                                                                 {
                                                                     Season = sCounter.ToString(),
                                                                     Episode = epnum,
                                                                     Name = eptitle,
                                                                     AirDate = epdate
                                                                 };

                                    eCounter++;
                                }
                            }

                            sCounter++;
                        }

                        return episodeArray.Where(item => item != null).ToArray();
                    }
                    
                    return null;
                }
                
                return null;
            }
            catch (XmlException e)
            {
                return null;
            }
        }

        /// <summary>
        /// Parses string to populate dictionary of key values
        /// </summary>
        /// <param name="res">Input string (delimited by \r\n)</param>
        /// <returns>Dictionary of values</returns>
        protected Dictionary<string, string> ParseOutputInformation(string res)
        {
            var delim = new string[1];
            delim[0] = "\r\n";
            var delim2 = new string[1];
            delim2[0] = "@";
            var resArr = res.Split(delim, StringSplitOptions.RemoveEmptyEntries);

            return resArr.Select(item => item.Split(delim2, StringSplitOptions.RemoveEmptyEntries)).Where(resArr2 => resArr2.Length == 2 && resArr2[0] != null && resArr2[1] != null && resArr2[0] != "" && resArr2[1] != "").ToDictionary(resArr2 => resArr2[0], resArr2 => resArr2[1]);
        }


        /// <summary>
        /// Gets HTML page from server
        /// </summary>
        /// <param name="url">URL of the page to get</param>
        /// <returns>String of HTML source</returns>
        protected string GetPage(string url)
        {
            WebResponse response = null;
            Stream stream = null;
            StreamReader reader = null;

            try
            {
                var request = (HttpWebRequest)WebRequest.Create(url);
                response = request.GetResponse();
                stream = response.GetResponseStream();

                if (!response.ContentType.ToLower().StartsWith("text/"))
                    return null;

                string buffer = "", line;
                reader = new StreamReader(stream);

                while ((line = reader.ReadLine()) != null)
                {
                    buffer += line + "\r\n";
                }

                return buffer;
            }
            catch (WebException e)
            {
                Console.WriteLine("Can't download:" + e);
                return null;
            }
            catch (IOException e)
            {
                Console.WriteLine("Can't download:" + e);
                return null;
            }
            finally
            {
                if (reader != null)
                    reader.Close();

                if (stream != null)
                    stream.Close();

                if (response != null)
                    response.Close();
            }
        }
    }
}
